package com.codemate.interview.java;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.thread.ThreadUtil;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 内存可见性
 *
 * @author hice
 * @apiNote 使用场景：多线程共享变量等
 * @apiNote 内存可见性、指令重排、JMM。 指令重排：为了性能考虑，编译器和CPU可能会对指令进行“保证语义”的重排工作。
 */
public class VolatileDemo {

    public static void main(String[] args) {
        // 展示volatile未使用时的多线程变量不一致问题
        volatileTest();
    }

    private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor(
        Runtime.getRuntime().availableProcessors(), 111,
        0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2048),
        new ThreadFactoryBuilder().setNamePrefix("volatile-pool-%d").build(),
        new ThreadPoolExecutor.AbortPolicy());

    /**
     * 多线程时读写无法保证原子性
     *
     * @apiNote 加上volatile时，写操作之前和之后会分别插入一个StoreStore屏障和StoreLoad屏障，禁止CPU对指令的重排序越过这些屏障。
     */
    private static /*volatile*/ boolean flag = true;

    /**
     * 可见性测试
     * <p> 无volatile时，线程2修改flag后，线程1无法感知到，所以一直等待。
     * <p> 有volatile时，线程2修改flag后，线程1可以感知到，所以会跳出while循环
     */
    private static void volatileTest() {
        THREAD_POOL.submit(() -> {
            while (flag) {
                // 等待flag!=true时跳出循环
                // System.out.println(Thread.currentThread().getName() + "等待flag!=true时跳出循环");
            }
            System.out.println(Thread.currentThread().getName() + "跳出循环了");
        });
        ThreadUtil.sleep(1000);
        THREAD_POOL.submit(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
            flag = false;
            System.out.println(Thread.currentThread().getName() + "flag被改成false");
        });
    }

}
